package com.superb.common.database.interceptor;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.superb.common.core.enums.DataScope;
import com.superb.common.core.enums.SuperbCode;
import com.superb.common.core.exception.SuperbException;
import com.superb.common.core.model.Result;
import com.superb.common.database.mybatis.expression.SuperbExpressionList;
import com.superb.common.database.mybatis.expression.SuperbInExpression;
import com.superb.common.security.entity.SuperbUserDataScope;
import com.superb.common.security.feign.RemoteSecuritySystemService;
import com.superb.common.utils.AuthDataScopeUtils;
import com.superb.common.utils.HeardersUtils;
import com.superb.common.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

/**
 * 数据权限拦截器,只针对查询的数据
 *
 * @Author: ajie
 * @CreateTime: 2024-07-12 09:59
 */
@Slf4j
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SuperbDataScopeInterceptor extends JsqlParserSupport implements Interceptor {

    private static final RemoteSecuritySystemService securitySystemService = SpringContextUtils.getBean(RemoteSecuritySystemService.class);

    /**
     * 部门数据范围字段
     */
    private static final String ORGAN_FIELD_ID = "organ_id";

    /**
     * 数据权限具体实现
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获得sql执行器
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        // mybatis反射工具类，根据sql执行器获取执行信息
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        // 获取mapper执行信息
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // 如果mapper执行select操作，那么开始进行数据权限拦截
        if (SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            DataScope dataScope = AuthDataScopeUtils.dataScope();
            // 如果当前执行线程中的方案没有设置权限注解，并且不是全部和不设置数据权限，那么就进入 数据权限拦截器
            if (dataScope != null && dataScope != DataScope.ALL && dataScope != DataScope.NONE) {
                // 获取sql解析器
                BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
                // 获取select操作集合
                Select selectStatement = (Select) CCJSqlParserUtil.parse(boundSql.getSql());
                // 获取select执行器
                PlainSelect select = selectStatement.getPlainSelect();
                // 获取主表别名
                String tableAliasName = "";
                FromItem fromItem = select.getFromItem();
                if (fromItem.getAlias() != null) {
                    tableAliasName = fromItem.getAlias().getName() + StringPool.DOT;
                }
                // 获取查询 where条件
                Expression where = select.getWhere();
                Expression binaryExpression = null;
                // TODO 获取用户数据权限范围
                switch (dataScope) {
                    case CUSTOM -> {
                        Result<SuperbUserDataScope> result = securitySystemService.getUserDataScope();
                        if (result.getCode() == 200 && result.isSuccess()) {
                            SuperbUserDataScope data = result.getResult();
                            SuperbExpressionList list = new SuperbExpressionList();
                            String[] organIds = data.getDataScopeOrganId().split(",");
                            // 自定义数据权限范围类型  0本部门；1本部门及子部门
                            if (data.getDataScopeOrganType() == 0) {
                                for (String organId : organIds) {
                                    list.addExpression(new StringValue(organId));
                                }
                            } else {
                                Set<String> subOrganIds = new HashSet<>();
                                // 获取所有权限范围内的本部门及子部门
                                for (String organId : organIds) {
                                    Result<List<String>> organizationIds = securitySystemService.getOrganizationIds(organId);
                                    if (organizationIds.getCode() == 200 && organizationIds.isSuccess()) {
                                        List<String> stringValues = organizationIds.getResult();
                                        subOrganIds.addAll(stringValues);
                                    }
                                }
                                for (String subOrganId : subOrganIds) {
                                    list.addExpression(new StringValue(subOrganId));
                                }
                            }
                            // in连接
                            SuperbInExpression inExpression = new SuperbInExpression();
                            inExpression.setLeftExpression(new Column(tableAliasName + ORGAN_FIELD_ID));
                            inExpression.setRightExpression(list);
                            binaryExpression = inExpression;
                        } else {
                            throw new SuperbException(SuperbCode.DATA_SCOPE_ORGAN, result.getMessage());
                        }
                    }
                    case ORGAN_AND_SUB, ORGAN_SUB -> {
                        Result<List<String>> result = securitySystemService.getOrganizationIds(HeardersUtils.getOrganizetionId());
                        if (result.getCode() == 200 && result.isSuccess()) {
                            List<String> data = result.getResult();
                            // 子部门-不包含本部门
                            if (dataScope == DataScope.ORGAN_SUB) {
                                data.remove(0);
                            }
                            InExpression inExpression = new InExpression();
                            inExpression.setLeftExpression(new Column(tableAliasName + ORGAN_FIELD_ID));
                            inExpression.setRightExpression(new SuperbExpressionList(data));
                            binaryExpression = inExpression;
                        } else {
                            throw new SuperbException(SuperbCode.DATA_SCOPE_ORGAN, result.getMessage());
                        }
                    }
                    case ORGAN -> {
                        EqualsTo equalsTo = new EqualsTo();
                        // 增加部门条件
                        equalsTo.setLeftExpression(new Column(tableAliasName + "organ_id"));
                        // 增加部门值
                        equalsTo.setRightExpression(new StringValue(HeardersUtils.getOrganizetionId()));
                        binaryExpression = equalsTo;
                    }
                    case USER -> {
                        EqualsTo equalsTo = new EqualsTo();
                        // 增加用户条件
                        equalsTo.setLeftExpression(new Column(tableAliasName + "create_by"));
                        // 增加当前用户值
                        equalsTo.setRightExpression(new StringValue(StpUtil.getLoginIdAsString()));
                        binaryExpression = equalsTo;
                    }
                    default -> {
                    }
                }
                // 将查询条件填充到select中
                if (where != null) {
                    select.setWhere(new AndExpression(new Parenthesis(where), binaryExpression));
                } else {
                    select.setWhere(binaryExpression);
                }
                metaObject.setValue("delegate.boundSql.sql", selectStatement.toString());
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
